/*
 * Copyright 2012 Walter Lütgenau.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package offset.nodes.client.editor.model;

import java.awt.Cursor;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.html.HTML;
import offset.nodes.client.editor.Editor;
import offset.nodes.client.editor.controller.EditorKit;
import offset.nodes.client.editor.controller.component.ComponentKit;
import offset.nodes.client.model.CommentedUserRequest;
import offset.nodes.client.view.ProgressDialog;
import offset.nodes.client.view.component.ComponentEditor;

/**
 *
 * @author
 * Walter
 * Lütgenau
 */
public class ComponentModel extends EditorModel {

    public static final String ATT_COMPONENT_ID = "n-component-id";
    public static final String ATT_COMPONENT_PATH = "n-component-path";
    Editor editor;

    /**
     * The model for components, as used by the actions.
     * 
     * @param context the model context
     * @param editor additional, the editor
     */
    public ComponentModel(DocumentContext context, Editor editor) {
        super(context);
        this.editor = editor;
    }
    
    /**
     * Return the component kit.
     * 
     * Some model methods have been implemented in the kit.
     * Make them available for the actions here.
     * 
     * @return the component kit
     */
    protected ComponentKit getComponentKit() {
        return ((EditorKit) getContext().getPane().getEditorKit()).getComponentKit();

    }

    /**
     * Used to delay the structural modification.
     * 
     * This will split the current paragraph element into two elements and insert the
     * component div element in between.
     */
    class InsertComponentRunner implements Runnable {

        Element element;
        int pos;
        String componentHtml;
        ProgressDialog progress;

        public InsertComponentRunner(Element element, int pos, String componentHtml) {
            this.element = element;
            this.pos = pos;
            this.componentHtml = componentHtml;
        }

        public void run() {
            try {
                splitElementAndInsertBetween(element, pos, componentHtml);
            } catch (BadLocationException ex) {
                Logger.getLogger(ComponentModel.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                Logger.getLogger(ComponentModel.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     * Add a component tag of the specified uuid or path at position pos.
     * 
     * As this may be called during a notification, the structural modification will be delayed using
     * SwingUtilities.invokeLater.
     * 
     * @param pos the position for the component
     * @param uuid the uuid of the component uuid tag
     * @param path alternatively, the path of the component path tag
     * @throws BadLocationException
     * @throws IOException 
     */
    public void insertComponent(int pos, String uuid, String path) throws BadLocationException, IOException {
        getContext().getPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

        StringBuilder buf = new StringBuilder();

        if (uuid != null)
            buf.append("<" + HTML.Tag.DIV.toString() + " " + ATT_COMPONENT_ID + "=\"" + uuid + "\"></" + HTML.Tag.DIV.toString() + ">");
        else
            buf.append("<" + HTML.Tag.DIV.toString() + " " + ATT_COMPONENT_PATH + "=\"" + path + "\"></" + HTML.Tag.DIV.toString() + ">");

        Element e = getDocument().getParagraphElement(pos);

        SwingUtilities.invokeLater(new InsertComponentRunner(e, pos, buf.toString()));
    }

    /**
     * Save all components.
     * 
     * @param comment used to indicate the kind of modification
     */
    public void saveComponents(CommentedUserRequest comment) {
        for (ComponentEditor component : getComponentKit().getComponentEditors()) {
            component.save(comment);
        }
    }
    
    /**
     * Exit all components without saving.
     * 
     * This may need some cleanup by the components.
     * 
     * @param comment used to indicate the kind of modification
     */
    public void exitComponents() {
        EditorKit kit = (EditorKit) getContext().getPane().getEditorKit();
        for (ComponentEditor component : kit.getComponentKit().getComponentEditors()) {
            component.exit();
        }
    }
    
    /**
     * Is this a component element?
     * 
     * @param element the element
     * @return the answer
     */
    public boolean isComponent(Element element) {
        return getComponentKit().isComponent(element);
    }
    
    /**
     * Grab the focus for the component pane associated to the element.
     * 
     * @param element the element with the component
     */
    public void grabFocus(Element element) {
        JComponent pane = getComponentKit().getComponentPane(element);
        if (pane != null)
            pane.grabFocus();
    }
}
